/// Platinum Net TCPChatServer.cpp : Chat Server used to test TCP Connection class.

#include <sstream>
#include <iostream>
#include <map>
#include "platinum.h"


#define DEFAULTPORT 12345

/**
*    ProcessError is a general purpose function intended to handle thrown exceptions by piping them to stderr
*/
int ProcessError(Platinum::Error e)
{
     std::cerr << "Error: "<< e.errCode << " " << e.errMessage << "\n";
     return(0);
}

int main(int argc, char* argv[])
{
     using namespace Platinum::Net;
     int iPort;
	 int naptime = 0;
	 int loopcounter = 1;
     bool quit = false;
     std::string IP,msg,newmsg;
     std::stringstream mybuf;
	 TCP::Connection* Remote;
	 Platinum::Net::TCP::ConnectionMap ClientMap;
	 TCP::ConnectionMap::iterator it;
	 std::vector<TCP::Connection*> vRemoveList;
	 std::vector<TCP::Connection*>::iterator RemoveIt;

     if(argc >= 2)
     {
          mybuf << argv[1]; 
          mybuf >> iPort;
     }else{
          iPort = DEFAULTPORT;
     }

     TCP::Connection* MyConn = new TCP::Connection();
     try
     {
	     TCP::Initialize(MyConn);
     }catch(Platinum::Error e){
	     ProcessError(e);
	     quit = true;
     }

     try
     {

#ifdef DEBUG
          std::cout << "Trying to bind to port " << iPort << std::endl;
#endif

          TCP::Bind(MyConn,iPort);
          std::cout << "Successfully bound to port " << iPort << std::endl;
          TCP::SetBlocking(MyConn,false);

     }catch(Platinum::Error e){
          ProcessError(e);
          quit = true;
     }

    while(!quit)
	{
          msg = newmsg;
          newmsg = "";
          
          try
          {
               Remote = TCP::Listen(MyConn);
          
          }catch(Platinum::Error e){
               ProcessError(e);
          }               

          if(Remote->Socket != MyConn->Socket)
          {
			   TCP::SetBlocking(Remote,false);
               if(ClientMap.find(Remote->Socket) == ClientMap.end())
               {
                    std::stringstream intro;
                    ClientMap[Remote->Socket] = Remote;
                    #ifdef DEBUG
                         std::cout << "Client connected from " << inet_ntoa(Remote->Addr.sin_addr) << std::endl;
                    #endif
                    msg += "Server: ";
                    msg += inet_ntoa(Remote->Addr.sin_addr);
                    msg += " has joined!\n";
                    
                    intro << "Welcome to our Chat server!\n"
                         << "Current users are\n";

                    ///Show List of Clients
                    for(it = ClientMap.begin(); it != ClientMap.end(); it++)                      
                             intro << inet_ntoa(it->second->Addr.sin_addr) << std::endl;

                    intro << std::endl;
                    TCP::Send(Remote,intro.str());

               }
          }
          ///Show List of Clients
                   // for(it = ClientMap.begin(); it != ClientMap.end(); it++)                      
					//	std::cout << inet_ntoa(it->second->Addr.sin_addr) << std::endl;
          
          ///Listen for new data
          for(it = ClientMap.begin();it != ClientMap.end(); it++)
          {
               Remote = it->second;
               try
               {
                    std::string tmp = TCP::Recv(Remote);
                    if(tmp != "" && tmp != "-1")
                    {
                         msg += inet_ntoa(Remote->Addr.sin_addr);
                         msg += ": ";
                         msg += tmp;
                         msg += "\n";
                    }
                    std::cout << msg;

               }catch(Platinum::Error e){
				  ProcessError(e);
                  if(e.errCode == EAGAIN || e.errCode == 0){
                         msg += "\n";
                         msg += inet_ntoa(Remote->Addr.sin_addr);
                         msg += " has left.\n";
						 vRemoveList.push_back(Remote);
			     }else{
                         ProcessError(e);
                 }
               }
          }

		//Send new data
          if(msg != "")
          {
               for(it = ClientMap.begin(); it != ClientMap.end(); it++)
               {
                    Remote = it->second;
			     if(Remote->Socket != MyConn->Socket)
                    {
                         try
                         {
                              if(!TCP::Send(Remote,msg))
                              {
                                   if(Remote != MyConn)
                                   {
                                        newmsg += "\n"; 
                                        newmsg += inet_ntoa(Remote->Addr.sin_addr);
                                        newmsg += " has left.\n";
										vRemoveList.push_back(Remote);
								   }
                              } 
          
                         }catch(Platinum::Error e){
                              if(e.errCode == EAGAIN)
                              {
                                   newmsg += "\n"; 
                                   newmsg += inet_ntoa(Remote->Addr.sin_addr); 
                                   newmsg += " has left.\n";
								   vRemoveList.push_back(Remote);
                              }else{
                                   ProcessError(e);
                              }
                         }
                    }
               }
          }
		  
		  while(!vRemoveList.empty())
		  {
			  
			  ClientMap.erase(vRemoveList.back()->Socket);
			  delete(vRemoveList.back());
			  vRemoveList.pop_back();
		  }

          ClientMap.begin() != ClientMap.end() ? naptime-- : naptime++;
		
          if(naptime > 30)
               naptime = 30;

          if(naptime < 1)
               naptime = 1;
#ifdef WIN32
          Sleep(naptime * 10);
#endif
#ifndef WIN32
          sleep(naptime * .1);
#endif
     }
}


